package ca.cbc.gisdigester.daemon;

import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.DateBuilder.nextGivenSecondDate;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

import java.text.ParseException;
import java.util.Date;

import org.apache.commons.daemon.Daemon;
import org.apache.commons.daemon.DaemonContext;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SchedulerMetaData;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ca.cbc.giscommon.util.GISConfig;

/**
 * This Class is the entry point for GIS daemon
 *
 * @author Liu Yang
 */
public class GISDaemon implements Daemon {
    private Logger log = LoggerFactory.getLogger(GISDaemon.class);
    private final static String MODULE_NAME = "LocalSrvExtDigester";
    private Scheduler sched;
    private final static String FEED_SYNC_CRON_EXPR = GISConfig.instance.getProperty("feed.sync.cron.expr");
    private final static String TWEET_SYNC_CRON_EXPR = GISConfig.instance.getProperty("tweet.sync.cron.expr");
    private final static String EPT_PURGE_CRON_EXPR = GISConfig.instance.getProperty("ept.purge.cron.expr");
    private final static String FREEMARKER_BAKE_CRON_EXPR = GISConfig.instance.getProperty("freemarker.bake.cron.expr");

    private void scheduleJob(Class<? extends Job> jobClass, String cronExpr, Date startTime) throws ParseException, SchedulerException {
        String jobName = jobClass.getSimpleName();
        //only will schedule the job if a cron expression is given
        if (cronExpr != null && !cronExpr.trim().equals("")) {
            // Init NewFileHandlerJob will only run every 30 seconds
            JobDetail job = newJob(jobClass)
                    .withIdentity(jobName, "group")
                    .build();

            CronTrigger trigger = newTrigger()
                    .withIdentity(jobName + "Trigger", "group")
                    .startAt(startTime)
                    .withSchedule(cronSchedule(cronExpr))
                    .build();

            // schedule the job to run
            Date scheduleTime = sched.scheduleJob(job, trigger);
            log.info(job.getKey() +
                    " will run at: " + scheduleTime +
                    " with cron expression: " + trigger.getCronExpression());
        }
    }

    private void initQuartzJob() throws Exception {
        log.info("*** Initializing Quartz Jobs ***");

        // First we must get a reference to a scheduler
        SchedulerFactory sf = new StdSchedulerFactory();
        sched = sf.getScheduler();

        log.info("*** Scheduling Jobs ************");

        // get a "nice round" time a few seconds in the future....
        Date startTime = nextGivenSecondDate(null, 10);

        // Init feed sync job
        scheduleJob(FeedSyncJob.class, FEED_SYNC_CRON_EXPR, startTime);

        // Init feed sync job
        scheduleJob(TweetSyncJob.class, TWEET_SYNC_CRON_EXPR, startTime);

        // Init ept pruge
        scheduleJob(EptPurgeJob.class, EPT_PURGE_CRON_EXPR, startTime);

        // Init author listing
        scheduleJob(FreemarkerBakeJob.class, FREEMARKER_BAKE_CRON_EXPR, startTime);
    }

    public void destroy() {
        log.info("============ Destroy module {} ============", MODULE_NAME);
    }

    public void init(DaemonContext arg0) throws Exception {
        log.info("------------ Initiating Module {} ---------", MODULE_NAME);

        try {
            //init quartz jobs
            initQuartzJob();
        } catch (Exception e) {
            log.error("Error", e);
            throw e;
        }

        log.info("============ Module {} Initiated ===========", MODULE_NAME);
    }

    public void start() throws Exception {
        log.info("------------ Starting Module {} ------------", MODULE_NAME);

        try {
            //start quartz jobs
            // All of the jobs have been added to the scheduler, but none of the jobs
            // will run until the scheduler has been started
            sched.start();
        } catch (Exception e) {
            log.error("Error", e);
            throw e;
        }

        log.info("============ Module {} started =============", MODULE_NAME);
    }

    public void stop() throws Exception {
        log.info("------------ Stopping Module {} ------------", MODULE_NAME);

        try {
            //stop quartz jobs
            log.info("*** Shutting Down *******");
            sched.shutdown(true);
            log.info("*** Shutdown Complete ***");
            SchedulerMetaData metaData = sched.getMetaData();
            log.info("*** Executed " + metaData.getNumberOfJobsExecuted() + " jobs.");
        } catch (Exception e) {
            log.error("Error", e);
            throw e;
        }

        log.info("============ Module {} stoped ==============", MODULE_NAME);
    }
}
